home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume91
/
devices
/
virtalph
/
part04
/
virtual.c
Wrap
C/C++ Source or Header
|
1991-05-08
|
59KB
|
2,161 lines
/*
about this project:
I started doing this thing in M.L. ( FindHandler is a remnant) but
then I realized I wanted to explore Handlers, not the Age of the
Universe.
But! luckily I encountered this fine piece of work by a
certain guy named *** Matt Dillion *** ; perhaps you've heard of him,
here's a guy who's making the best of the terrible misfortune to
have been born with eight arms; and the reflexes of a fly ;-)
yours truly anselm
*/
#define NOBECOLORS
#define BEVIRTUAL
#include "dos.h"
#define MakeID(a,b,c,d) ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) )
#define VIRT MakeID('V','I','R','T')
int myreadsome(ubyte *,FENTRY *,int);
int andycode(char *,MYFH *,PACKET *);
/*
THE FOLLOWING IS A REVIEW OF THE APPROACH I TOOK TO SOLVING THE
PROBLEM OF THIS HANDLER NOT COMPILING PROPERLY UNDER LATTICE 5.10
It should serve as general help on writing MANX compatible code,
code using the AMIGADOS CreateProc, and nailing bugs in LATTICE 5.10.
(this review was released as an informative guide to some of the
pitfalls of C programming, and as a bit of humour about just how
yuchy things can get sometimes :) Anselm Hook - Jan 21 1991. )
(this exploration was evolved over a 3 day period at about 8 hours
a day (my computer is very slow) (now i know why people use machine
code; comparatively in 3 days worth of time we mostly wrote DATASTORM
(admittedly at 24 hours a day but....) )
( I bothered with this analysis because of the inexplicable erratic
nature of the bugs, and the dependancy i was experiencing on
an enviroment i didn't fully understand...now that I've solved the
problem this analysis will service me in the future, and the general
knowledge i've acquired should help me design around potential
catastrophes like this; all of this applies particularily when i
try to do some really heavy coding in C++)).
compiling pitfalls:
GENERAL:
linker
VERBOSE NOALVS both have no effect
SC SD
*** if both are set then _sprintf attempts to use (a4) base
relative addressing. However base relative addressing
is not enabled, so the system crashes.
Presumably if the -y option is set in the program then
base relative addressing will magically become functional;
except that I'm not certain if the system has been given
space for _sprintf to use...which may mean that rather than
crashing right away it just trashes local memory for a while.
(we'll have to try -y, and SC, SD seperately and combined)
SC
WHEN REMOVED: (and -v -b0 -r are on)
- modifies the addresses accessed by (a4), however a4 is
still not initialized. Note that all of my code seems to
be OK, its just the linked in system support code that is
attempting to use the uninitialized a4.
- some symbols become indirect (less visible too)
SD
WHEN REMOVED: (and -v -b0 -r are on)
- does fix problem of code trying to use (a4)
- code seems to call the wrong addresses or something weird
- freezes up and dies when trying to "findport"
NODEBUG actually helps supply debug symbols, I don't know why...
ADDSYM must have NODEBUG present, and if NODEBUG is set then
this must be present or the file won't be executable
(for some reason).
in general doesn't bitch as profluently if lcm.lib is left out.
compiler
sometimes ABSEXECBASE is 0 - I don't know why, may be BLINKS fault
-r = pc relative addressing (but not base relative???)
seems to work when this is on.
-y = reload a4 always, seems to make package fail, i'm not
sure why, but i did notice that it did not set A4 as
it promised, furthermore that A4 was (or became required).
(actually this is due to SC SD i think)
-b0 = does something, I'm not exactly sure what
so far i haven't got it to work without -b0
Ok -> -b is ON by default, thats why -b has no effect, and
-b0 turns it off (base register relative mode addressing)
-b0 definately makes it not require a4,
ABSEXECBASE is wrong still however (a6).
(actually still requires A4 depending on SC SD setting)
-v = disable stack checking, does help (although I don't know why).
interestingly enough the linker will fail unless this option
is set. The linker appears to be looking for some variables
to give it position specific guidance if -v is on.
IF ONLY -V is set then the program will fail when it
trys to access relative to (a4).
-d = not important. but to access debugging info in general:
turn ADDSYM on in linker then:
debug on...this works ok, to trap the handler run 2 copies of
DB, do an 'al' on "cd device", then set a second 'al' on
second DB once first one has trapped the 'cd', then quit
the first one - the cd will run, and then try start up the
device process, at which point the second db catches it, and
if you are in the right directory it will load up the properly
named symbol table.
DIFFERENT ATTEMPTS:
one:
using -v -b0 -r and SC SD ADDSYM
with my code:
FINDPORT = hang point - doesn't reach debug window
without my code:
SPRINTF = hang point (missing (a4)) - gets as far as debug window
two:
using -v -b -r -y and ADDSYM SC SD (may change code size, erratic result?)
with my code:
(it should crash at FINDPORT still)
(no purpose in trying if below crashes)
without my code:
(it should work ok)
actually crashes still, still missing (a4) at _sprintf
three:
-v -r -y -b0 and ADDSYM SC SD ADDSYM
without my code:
still misses (a4) at sprintf
four:
-v -b0, SC SD (without my code)
- works, doesn't try to access a4
- obviously the -r is forcing a base relative addressing on.
- and -y just doesn't ever work.
- notably some tasks in system at same time crash on exiting
- notably (a4) seems to point to something although its not used
five:
-v -b0, SC SD ADDSYM (without my code)
- exactly same response as (four)
- (including types of crashes etc. DEBUG doesn't seem to have bad fx)
six:
-b0 SC SD ADDSYM (without my code)
- complains about missing link time stuff _StackPtr etc...
seven:
-v -b0, SC SD ADDSYM WITH-MY-CODE
crashes in this way:
_typetostr+12c
jsr _typetostr+18a
(sysbase is corrupt incidentally)
_pf+46
(crashes here, A4 is not initialized, yet required!)
(works fine from here on - if i force A4 to be valid)
_strupr+2
_dbinit
_typetostr+130
eight:
-v -b0 NO-MY-CODE, NO SC SC, JUST ADDSYMS
freezes up (at findport i believe) - forever.
nine:
-v -b0 ....no blink options, none of my code
freezes up exactly same... (thus ADDSYM definately has no effect)
BLINK states that SC SD makes any object module that was compiled
with -b use it. Even if I disable -b, blink is still going to
throw in some system modules using it if i set SC SD. This is
why code can be referencing (a4) when its not enabled by compiler.
Interestingly enough I noticed that _pf+46 was not supposed to
fall through to _strupr+2, when recompiled without SC SD, this
may be another bug. (actually i believe this is because i was
using an old symbol table, as i just found out)
Altogether there are then these bugs I have seen:
1) use of (a4) illegally
2) bad init on SysBase (probably not)
3) bad code (probably not actually)
4) freeze up waiting for port sig (bad sysbase?)
I have figured out:
1) kindof my fault, anyway I can avoid it
2) no idea? perhaps debugger is showing wrong address? (no see 3)
3) no idea? perhaps compiler bug? (is bad symbol table)
4) perhaps bad sysbase?
ok so bugs are:
1) (a4) use...fixed
4) freeze waiting for port -> createproc failed?
Notably the reason it doesn't run with my code, and crashes externals
is due to (a4) causing erratic crash behavior. That is definately
fixed now. It would be nice to have a proper A4 however, and possibly
i can try use the -y to force it on, or check some code from the
lattice support startup source to see how they do it properly.
unknown:
major is why should it freeze waiting for port sig if
the silly SC SD is not set? its gotta be a sysbase problem
cause sysbase was corrupt before, but maybe not needed.
*** NEWS BULLETIN ***
the reason for bug #4 is very very bad....
Matt patched in a hunk of machine code such that no matter what
happened the BCPL<<C segment would hit approximately the right
area and get funneled off to where it belonged.
When I *don't* use a SMALLCODE SMALLDATA model, the operation
gets indexed through a JUMP TABLE!!!! and of course %78 of the
time it fucks up; the BCPL<<C being potentially off 2 in either
direction...at run time...
so - i know you're all waiting in breathless anticipation -
how do i solve it!!!?
maybe ARP has a createproc of its own... [ nope ]
hmmm...
[ TECHNICALLY THIS IS A BUG IN LATTICE 5.10 for permitting jump
tables on non-longword aligned addresses - because surely DOS
must load longword aligned? (now that i think about it) ]
i guess maybe what lattice does is just keep functions longword
aligned? (lets check) [ NOPE ]
[ this is probably why historically I've never been able to
get CreateProc to work, actually when I first wanted to try
a "neato" function like that I presumably didn't even cast
the function pointer as a BCPL... ]
an easy way to check if the bug has been resolved is to
just look at _debugproc
- we basically must remove this call from the jump table...
- _builtin_ is a way [ but its a mess! ]
- r may solve this...but lets really deal with it... [ NAH JUST DO IT ]
[ -r does not remove the jump table...therefore it doesn't help ]
- lets try base relative? (just once?)
ok config is now:
-v -y -r, ADDSYM, MY-CODE-ON (and -b on by default)
- now SysBase is wrong *AND* debugproc still jumps via library ref!!!
(why the fuck is SysBase wrong???)
SysBase is wrong because - get this - its accessed off of (A4) now,
and *(4(a4)) is something other than *(4(0))
ABSEXECBASE became a *variable* with an ABSOLUTE delta offset of
4 into the program data block; versus being ABSOLUTE in the vernacular
logically correct sense...it has choosen to a be groovier, cooler
kind of ABSEXECBASE than all those other squares...
This is partly Matts fault because of his damned "void *"'s but
lattice clearly made a compile mistake.
OK, lets just fix SysBase, and turn on SC SD to hopefully kill the
jump table...
*Y*E*S* FUCK ME BLUE it worked....
Ok so the jump table is axed, and the only system reference that
I encountered is fixed...hopefully there aren't any other defines
like that...[ there appear not to be any ].
Ok now the bugs were:
1) ABSEXECBASE was referenced as an external void *; which
to lattice could mean fucking anything...so naturally it
became consumed into the compiler as a "variable" versus
whatever it really was.
Despite understanding it, i would still say this is a bug
in lattice 5.10 (mostly).
2) The Jump Tables. Should be longword aligned. I would
say that this is a bug; because its something unexpected
and non-obvious to the casual user. Especially in an
Amiga Enviroment where BCPL DOS requires longword
alignment. So this is also a bug in Lattice 5.10.
*** the end of this 3 day debug session ***
Now we have a bug where:
my readsome bad values, as compared to submitted values
possible problems:
1) void * (again) treated as not long sometimes?
but then it should freak everywhere
2) structure passed on stack, not just address
BUG: DOSDevice isn't pointed right from M.L...
doesn't handle pc relative?
* DOSDEVICE.C V1.10 2 November 1987
*
* EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C PUBLIC DOMAIN.
*
* By Matthew Dillon.
*
* Debugging routines are disabled by simply attempting to open the
* file "debugoff", turned on again with "debugon". No prefix may be
* attached to these names (you must be CD'd to TEST:).
*
* See Documentation for a detailed discussion.
*
* BUGS:
* Currently the only known bug is with the implementation of the
* RAM disk itself. Specifically, if filehandle A is at the end of
* the file, and somebody appends to the file with another filehandle,
* B, filehandle A will get confused as to it's current position in
* the file.
*
* I am probably not updating all the right timestamps. This is
* easy to fix... All you have to do is fool with the floppy and
* see which datestamps get updated for certain operations.
*/
/*
* Since this code might be called several times in a row without being
* unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!! This also goes
* for any global/static assignments that might be changed by running the
* code.
*/
#ifdef BEVIRTUAL
int Virtual = 1; /* default on!!! */
#endif
PROC *DosProc; /* Our Process */
DEVNODE *DosNode; /* Our DOS node.. created by DOS for us */
DEVLIST *DevList; /* Device List structure for our volume node */
void *SysBase; /* EXEC library base */
DOSLIB *DOSBase; /* DOS library base for debug process */
RAMFILE RFRoot; /* Directory/File structure (root node) */
LIST FHBase; /* Open Files */
LIST LCBase; /* Open Locks */
long TotalBytes; /* total bytes of data in filesystem */
/* DEBUGGING */
PORT *Dbport = 0; /* owned by the debug process */
PORT *Dback = 0; /* owned by the DOS device driver */
short DBDisable;
MSG DummyMsg; /* Dummy message that debug proc can use */
/*
* Don't call the entry point main(). This way, if you make a mistake
* with the compile options you'll get a link error.
*/
void
noname()
{
register PACKET *packet;
register short error;
MSG *msg;
ubyte notdone;
ubyte buf[256];
void *tmp;
ULONG *wowsers = 0x4;
#ifdef BECOLORS
UWORD *colorptr = 0xdff180;
UWORD i;
for(i=0;i<300;i++) {
*colorptr = i;
};
#endif
/*
* Initialize all global variables. SysBase MUST be initialized before
* we can make Exec calls. AbsExecBase is a library symbol
* referencing absolute memory location 4. The DOS library is opened
* for the debug process only.
*/
DBDisable = 1; /* Init. globals */
Dbport = Dback = NULL;
TotalBytes = 0;
SysBase = *(wowsers); /* AbsExecBase; not anymore AH 91 */
DOSBase = OpenLibrary("dos.library",0);
DosProc = FindTask(NULL);
{
WaitPort(&DosProc->pr_MsgPort); /* Get Startup Packet */
msg = GetMsg(&DosProc->pr_MsgPort);
packet = (PACKET *)msg->mn_Node.ln_Name;
/*
* Loading DosNode->dn_Task causes DOS *NOT* to startup a new
* instance of the device driver for every reference. E.G. if
* you were writing a CON device you would want this field to
* be NULL.
*/
if (DOSBase) {
DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
register DEVLIST *dl = dosalloc(sizeof(DEVLIST));
DosNode = BTOC(packet->dp_Arg3);
/*
* Create Volume node and add to the device list. This will
* cause the WORKBENCH to recognize us as a disk. If we don't
* create a Volume node, Wb will not recognize us. However,
* we are a RAM: disk, Volume node or not.
*/
DevList = dl;
dl->dl_Type = DLT_VOLUME;
dl->dl_Task = &DosProc->pr_MsgPort;
dl->dl_DiskType = ID_DOS_DISK;
dl->dl_Name = (void *)DosNode->dn_Name;
dl->dl_Next = di->di_DevInfo;
di->di_DevInfo = (long)CTOB(dl);
/*
* Set dn_Task field which tells DOS not to startup a new
* process on every reference.
*/
DosNode->dn_Task = &DosProc->pr_MsgPort;
packet->dp_Res1 = DOS_TRUE;
packet->dp_Res2 = 0;
} else { /* couldn't open dos.library */
packet->dp_Res1 = DOS_FALSE;
returnpacket(packet);
return; /* exit process */
}
returnpacket(packet);
}
/*
* Initialize debugging code
*/
if(!DBDisable)dbinit();
/* Initialize RAM disk */
{
ubyte *ptr = BTOC(DosNode->dn_Name);
short len = *ptr;
NewList(&FHBase); /* more globals */
NewList(&LCBase);
bzero(&RFRoot,sizeof(RFRoot));
RFRoot.type = FILE_DIR; /* root directory */
DateStamp(&RFRoot.date); /* datestamp */
NewList(&RFRoot.list); /* sub dirs */
RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /* Root NAME */
bmov(ptr+1,RFRoot.name,len);
RFRoot.name[len] = 0;
dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name);
}
/*
* Here begins the endless loop, waiting for requests over our
* message port and executing them. Since requests are sent over
* our message port, this precludes being able to call DOS functions
* ourselves (that is why the debugging routines are a separate process)
*/
top:
for (notdone = 1; notdone;) {
WaitPort(&DosProc->pr_MsgPort);
#ifdef BECOLORS
for(i=0;i<300;i++) {
*colorptr = i;
};
#endif
while (msg = GetMsg(&DosProc->pr_MsgPort)) {
register ubyte *ptr;
#ifdef BECOLORS
for(i=0;i<300;i++) {
*colorptr = i;
};
#endif
packet = (PACKET *)msg->mn_Node.ln_Name;
packet->dp_Res1 = DOS_TRUE;
packet->dp_Res2 = 0;
error = 0;
dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
packet->dp_Type,
packet->dp_Arg1, packet->dp_Arg2,
packet->dp_Arg3,
typetostr(packet->dp_Type)
);
/*#ifdef BEDEVICE*/
switch(packet->dp_Type) {
case ACTION_DIE: /* attempt to die? */
notdone = 0; /* try to die */
break;
case ACTION_OPENRW: /* FileHandle,Lock,Name Bool */
case ACTION_OPENOLD: /* FileHandle,Lock,Name Bool */
case ACTION_OPENNEW: /* FileHandle,Lock,Name Bool */
{
register RAMFILE *ramfile;
RAMFILE *parentdir = getlockfile(packet->dp_Arg2); /* passive AH */
char *ptr;
#ifdef BEVIRTUAL
ulong *virtual_device = 0;
ulong *virtual_fh = 0;
int VirtualOpen = 0;
#endif
btos(packet->dp_Arg3,buf);
dbprintf("'%s' ", buf);
if (strcmp(buf,"debugoff") == 0)
{ DBDisable = 1; dbuninit(); }
if (strcmp(buf,"debugon") == 0)
{ DBDisable = 0; dbinit(); }
#ifdef BEVIRTUAL
if (strcmp(buf,"virtualon") == 0) /* debugging */
{
dbprintf("Virtual is on\n");
Virtual = 1;
}
if (strcmp(buf,"virtualoff") == 0)
{
dbprintf("Virtual is off\n");
Virtual = 0;
}
#endif
/* is found? */
if (ramfile = searchpath(&parentdir,buf,&ptr))
{
if (ramfile->type == FILE_DIR)
{ error=ERROR_OBJECT_WRONG_TYPE; goto openbreak;}
if (ramfile->locks < 0)
{ error = ERROR_OBJECT_IN_USE; goto openbreak;}
if (packet->dp_Type == ACTION_OPENOLD)
{
++ramfile->locks; /* fall thru to last bundle */
#ifdef BEVIRTUAL
/*
this routine may abort rest of read attempt
note that i've checked above code to make
sure that nothing was initialized...so its
ok to just "step out" as it were AH Jan2291
*/
if (Virtual)
{
UBYTE mtemp[VIBLEN+512];
DEVNODE *MyDevNode;
/*VIB *vib;*/
int len;
len = myreadsome(mtemp,GetHead(&(ramfile->list)),VIBLEN+512);
if(!len)
{
dbprintf("not virtual file, treating as normal\n");
goto BENORMAL;
}
dbprintf("virtual: name to find is %s\n",mtemp+VIBLEN);
if(!(MyDevNode = FindHandler(mtemp+VIBLEN,DOSBase,mtemp+VIBLEN-20))) /* and key */
{
/* ask user to insert disk please */
/* if user cancels then say: */
/* wait for reply, or search again myself */
/* if user does insert I would like to lock it */
dbprintf("device %s not found\n",mtemp+VIBLEN);
error = ERROR_OBJECT_NOT_FOUND;
--ramfile->locks;
goto openbreak;
}
else
{
PACKET copypacket;
/* gee i really hope that "buffer" is longword aligned!!!! */
UBYTE buffer[512];
dbprintf("device %s was found %lx\n",mtemp+VIBLEN,MyDevNode->dn_Task);
virtual_device = MyDevNode->dn_Task;
/*
arg1 = filehandle (to fill)
arg2 = lock (i'll zero this cause there is no parent - i pass entire path!)
arg3 = name (to find) [as a BSTR!]
*/
bmov(packet,©packet,sizeof(PACKET));
/* copypacket.dp_Arg1 = packet->dp_Arg1...*/
copypacket.dp_Arg2 = 0; /* no parent lock! */
buffer[0] = strlen(mtemp+VIBLEN);
strncpy(&(buffer[1]),mtemp+VIBLEN,512-2);
copypacket.dp_Arg3 = CTOB(&(buffer[0]));
if(!(sendpacket(©packet,virtual_device)))
{
error = ERROR_OBJECT_NOT_FOUND;
--ramfile->locks;
goto openbreak;
}
dbprintf("001: packet sent!\n");
if(!copypacket.dp_Res1)
{
error = copypacket.dp_Res2;
--ramfile->locks;
goto openbreak;
}
dbprintf("002: no error\n");
/* get and stash the real file handle */
virtual_fh = ((FH *)BTOC(packet->dp_Arg1))->fh_Arg1;
dbprintf("file handle is %ld\n",virtual_fh);
/*
i call other device, it takes over fh->arg1
i copy fh->arg1 away (may check if 0??? (no should leave same)
i call vir, it takes over fh->arg1 (again)
*/
VirtualOpen = 1;
/*
open
- copy the packet (or make a packet)
- set FileHandle to packet->arg1 (FileHandle) (in copy also)
- replace name field of packet (packet->arg3) (in copy only)
- set lock to 0 (in copy only)
- submit packet to device
- wait for reply
- if failed then error = its error, goto openbreak;
- save for a moment DeviceID & FileHandle, set flag *special*
- at bottom of open: if *special* set then copy DeviceID/FileHandle in
(just override parts of MYFH because it shouldn't be used outside
of READ/WRITE [ SEEK/CLOSE ] and if a file handle is in virtual
mode it stays that way, regardless of if the handler is in
virtual mode or not.
*** general; we should think about the visibility of the virtual
flag, and if it conflicts with re-entrant nature of these
routines -> i think it does?)
in which case we should have special packets instead
*/
}
} /* endif virtual */
BENORMAL:
#endif
goto success;
}
/* end of open_old */
else /* packet request is NOT OPEN OLD */
{
/* shared read only? */
if (ramfile->locks > 0)
{
error = ERROR_OBJECT_IN_USE;
}
else
{
/* new open overtop old file */
if (packet->dp_Type == ACTION_OPENNEW) {
freedata(ramfile);
ramfile->protection = 0;
}
--ramfile->locks;
}
}
}
else /* file is not found */
{
if (!parentdir) {
error = ERROR_INVALID_COMPONENT_NAME;
goto openbreak;
}
if (packet->dp_Type == ACTION_OPENNEW) {
ramfile = createramfile(parentdir, FILE_FILE, ptr);
--ramfile->locks;
} else
{ error = ERROR_OBJECT_NOT_FOUND; }
}
/* open new and open old success hit here */
success:
/*
OPEN_NEW was passed a "FileHandle"; in the file handle there is
a slot for matt to stick in his "myfh"; which he then does; when we
want to patch the requests on we will pass the supplied file handle
onto the next device, and then swap its sub_arg with ours, such that
we can handle any further requests....swapping as needed.
*/
if (!error)
{
register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR);
((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long)mfh;
mfh->file = ramfile;
mfh->fentry = GetHead(&ramfile->list);
#ifdef BEVIRTUAL
if(!VirtualOpen)
{
mfh->device = 0; /* Virtual AHJan2391 */
mfh->devfh = 0;
}
else
{
mfh->device = virtual_device;
mfh->devfh = virtual_fh;
}
#endif
AddHead(&FHBase,mfh);
}
}
openbreak:
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register FENTRY *fen = mfh->fentry;
register ubyte *ptr = (ubyte *)packet->dp_Arg2;
register long left = packet->dp_Arg3;
register long scr;
if(andycode("read",mfh,packet))break;
while (left && fen) {
scr = fen->bytes - mfh->offset;
if (left < scr) {
bmov(fen->buf + mfh->offset, ptr, left);
mfh->offset += left;
left = 0;
} else {
bmov(fen->buf + mfh->offset, ptr, scr);
left -= scr;
ptr += scr;
mfh->base += fen->bytes;
mfh->offset = 0;
fen = NextNode(fen);
}
}
mfh->fentry = fen;
packet->dp_Res1 = packet->dp_Arg3 - left;
}
break;
case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register FENTRY *fen = (FENTRY *)mfh->fentry;
ubyte *ptr = (ubyte *)packet->dp_Arg2;
long left = packet->dp_Arg3;
long scr;
if(andycode("write",mfh,packet)) break;
/*
* Doesn't work right if multiple readers/appenders.
*/
while (left) {
if (fen) {
dbprintf("FEN: %ld left: %ld\n", fen->bytes, left);
scr = fen->bytes - mfh->offset;
if (left < scr) {
if (fen->bytes < mfh->offset + left)
dbprintf("PANIC! AWR0\n");
else
bmov(ptr, fen->buf + mfh->offset, left);
mfh->offset += left;
left = 0;
} else {
if (fen->bytes < mfh->offset + scr)
dbprintf("PANIC! AWR1\n");
else
bmov(ptr, fen->buf + mfh->offset, scr);
ptr += scr;
left -= scr;
mfh->base += fen->bytes;
mfh->offset = 0;
fen = NextNode(fen);
}
} else {
fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC);
if (fen->buf = AllocMem(left, MEMF_PUBLIC)) {
fen->bytes = left;
mfh->file->bytes += left;
mfh->base += left;
mfh->offset = 0;
TotalBytes += left;
AddTail(&mfh->file->list, fen);
dbprintf("NEWFEN: (%ld)\n", fen->bytes);
bmov(ptr, fen->buf, left);
left = 0;
} else {
FreeMem(fen, sizeof(FENTRY));
dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left);
mfh->offset = 0;
break;
}
fen = NULL; /* cause append */
}
}
packet->dp_Res1 = packet->dp_Arg3 - left;
mfh->fentry = fen;
}
break;
case ACTION_CLOSE: /* FHArg1 Bool:TRUE */
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register RAMFILE *file = mfh->file;
andycode("close",mfh,packet);
Remove(mfh);
FreeMem(mfh,sizeof(*mfh));
if (--file->locks < 0)
file->locks = 0;
}
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition*/
{
register MYFH *mfh = (MYFH *)packet->dp_Arg1;
register FENTRY *fen;
register long absseek;
if(andycode("seek",mfh,packet))break;
packet->dp_Res1 = mfh->base + mfh->offset;
absseek = packet->dp_Arg2;
if (packet->dp_Arg3 == 0)
absseek += mfh->base + mfh->offset;
if (packet->dp_Arg3 == 1)
absseek = mfh->file->bytes + absseek;
if (absseek < 0 || absseek > mfh->file->bytes) {
error = ERROR_SEEK_ERROR;
break;
}
mfh->base = mfh->offset = 0;
/*
* Stupid way to do it but....
*/
for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) {
if (mfh->base + fen->bytes > absseek) {
mfh->offset = absseek - mfh->base;
break;
}
mfh->base += fen->bytes;
}
mfh->fentry = fen;
}
break;
/*
* This implementation sucks. The right way to do it is with
* a hash table. The directory must be searched for the file
* name, then the next entry retrieved. If the next entry is
* NULL there are no more entries. If the filename could not
* be found we return the first entry, if any.
*
* You can't simply keep a pointer around to the next node
* because it can be moved or removed at any time.
*/
case ACTION_EXAMINE_NEXT: /* Lock,Fib Bool */
{
register FIB *fib = BTOC(packet->dp_Arg2);
register RAMFILE *dir = getlockfile(packet->dp_Arg1);
register RAMFILE *file;
long *blah;
if (dir->type == FILE_FILE) {
error = ERROR_OBJECT_WRONG_TYPE;
break;
}
file = GetHead(&dir->list);
blah = &(fib->fib_Comment[68]);
fib->fib_Comment[67] = 0;
/* if(fib->fib_DiskKey) { */
if (*blah) {
register int len = *(ubyte *)fib->fib_FileName;
for (; file; file = NextNode(file)) {
if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len))
break;
}
if (file)
file = NextNode(file);
else
file = GetHead(&dir->list);
}
*blah = 1;
/* fib->fib_DiskKey = 1; */
error = -1;
if (!(tmp=file)) {
error = ERROR_NO_MORE_ENTRIES;
break;
}
}
/* fall through */
case ACTION_EXAMINE_OBJECT: /* Lock,Fib Bool */
{
register FIB *fib;
register RAMFILE *file;
register RAMFILE *dummy;
fib = BTOC(packet->dp_Arg2);
if (error) {
file = tmp; /* fall through from above */
} else {
file = getlockfile(packet->dp_Arg1);
fib->fib_Comment[67] = 0;
fib->fib_Comment[68] = 0;
fib->fib_Comment[69] = 0;
fib->fib_Comment[70] = 0;
fib->fib_Comment[71] = 0;
/* fib->fib_DiskKey = 0; */
}
error = 0;
fib->fib_DirEntryType = file->type;
strcpy(fib->fib_FileName+1, file->name);
fib->fib_FileName[0] = strlen(file->name);
fib->fib_Protection = file->protection;
fib->fib_EntryType = file->type;
{
long *blah;
blah = (long *)(&(fib->fib_DiskKey));
*blah = (long *)file;
/* every file must have a unique KEY!!! */
/* AmigaDOS and I both use this scheme to
make sure 2 files are not same instance */
}
fib->fib_Size = file->bytes;
fib->fib_NumBlocks = (fib->fib_Size / 512);
fib->fib_Date = file->date;
if (file->comment) {
/*!!AH!*/ strncpy(fib->fib_Comment+1, file->comment,65);
fib->fib_Comment[0] = strlen(file->comment);
} else {
fib->fib_Comment[0] = 0;
}
#ifdef BEVIRTUAL
if (Virtual && file->type < 0) {
VIB vib;
int x; long *myptr;
x = myreadsome(&vib,GetHead(&(file->list)),VIBLEN);
dbprintf("coolo %ld %ld\n",x,VIBLEN);
if(!x) {
dbprintf("examine: file is not virtual");
goto JUSTNORMAL;
}
fib->fib_Size = vib.Size;
fib->fib_NumBlocks = (fib->fib_Size / 512);
/* information that must be returned to the coalesce utility,
external to the handler...just a checksum... */
fib->fib_Comment[67] = 0;
myptr = &(fib->fib_Comment[72]);
*(myptr) = VIRT;
*(myptr+1) = vib.CheckSum;
}
JUSTNORMAL:
#endif
}
break;
case ACTION_INFO: /* Lock, InfoData Bool:TRUE */
tmp = BTOC(packet->dp_Arg2);
error = -1;
/* fall through */
case ACTION_DISK_INFO: /* InfoData Bool:TRUE */
{
register INFODATA *id;
/*
* Note: id_NumBlocks is never 0, but only to get
* around a bug I found in my shell (where I divide
* by id_NumBlocks). Other programs probably break
* as well.
*/
(error) ? (id = tmp) : (id = BTOC(packet->dp_Arg1));
error = 0;
bzero(id, sizeof(*id));
id->id_DiskState = ID_VALIDATED;
id->id_NumBlocks = (TotalBytes >> 9) + 1;
id->id_NumBlocksUsed = (TotalBytes >> 9) + 1;
id->id_BytesPerBlock = 512;
id->id_DiskType = ID_DOS_DISK;
id->id_VolumeNode = (long)CTOB(DosNode);
id->id_InUse = (long)GetHead(&LCBase);
}
break;
case ACTION_PARENT: /* Lock ParentLock */
{
register RAMFILE *file = getlockfile(packet->dp_Arg1);
if (file->type == FILE_FILE) {
error = ERROR_OBJECT_NOT_FOUND;
break;
}
if (file->locks < 0) {
error = ERROR_OBJECT_IN_USE;
break;
}
if (file->parent)
packet->dp_Res1 = (long)CTOB(ramlock(file->parent, ACCESS_READ));
else
error = ERROR_OBJECT_NOT_FOUND;
}
break;
case ACTION_DELETE_OBJECT: /*Lock,Name Bool */
{
RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
RAMFILE *ramfile;
btos(packet->dp_Arg2, buf);
if (ramfile = searchpath(&parentdir,buf,NULL)) {
if (ramfile->locks || ramfile == &RFRoot) {
error = ERROR_OBJECT_IN_USE;
break;
}
if (ramfile->type == FILE_DIR) {
if (GetHead(&ramfile->list))
error = ERROR_DIRECTORY_NOT_EMPTY;
} else {
freedata(ramfile);
}
if (!error) {
freeramfile(ramfile);
DateStamp(&parentdir->date);
}
} else {
if (!parentdir)
error = ERROR_INVALID_COMPONENT_NAME;
else
error = ERROR_OBJECT_NOT_FOUND;
}
}
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_CREATE_DIR: /* Lock,Name Lock */
{
RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
RAMFILE *ramfile;
char *ptr;
btos(packet->dp_Arg2, buf);
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
error = ERROR_OBJECT_EXISTS;
break;
}
if (!parentdir) {
error = ERROR_INVALID_COMPONENT_NAME;
break;
}
ramfile = createramfile(parentdir, FILE_DIR, ptr);
packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE));
}
break;
case ACTION_LOCATE_OBJECT: /* Lock,Name,Mode Lock */
{
RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
RAMFILE *ramfile;
btos(packet->dp_Arg2, buf);
dbprintf("'%s' %ld ", buf, packet->dp_Arg3);
if (ramfile = searchpath(&parentdir,buf,NULL)) {
if (ramfile->locks < 0 || (ramfile->locks && packet->dp_Arg3 == ACCESS_WRITE)) {
error = ERROR_OBJECT_IN_USE;
break;
}
packet->dp_Res1 = (long)CTOB(ramlock(ramfile, packet->dp_Arg3));
} else {
if (!parentdir)
error = ERROR_INVALID_COMPONENT_NAME;
else
error = ERROR_OBJECT_NOT_FOUND;
}
}
break;
case ACTION_COPY_DIR: /* Lock, Lock */
{
register RAMFILE *ramfile = getlockfile(packet->dp_Arg1);
if (ramfile->locks < 0)
error = ERROR_OBJECT_IN_USE;
else
packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_READ));
}
break;
case ACTION_FREE_LOCK: /* Lock, Bool */
if (packet->dp_Arg1);
ramunlock(BTOC(packet->dp_Arg1));
if (!GetHead(&FHBase) && !GetHead(&LCBase))
notdone = 0;
break;
case ACTION_SET_PROTECT:/* -,Lock,Name,Mask Bool */
{
register RAMFILE *ramfile;
RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
char *ptr;
btos(packet->dp_Arg3, buf);
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
ramfile->protection = packet->dp_Arg4;
} else {
if (parentdir)
error = ERROR_OBJECT_NOT_FOUND;
else
error = ERROR_INVALID_COMPONENT_NAME;
}
}
break;
case ACTION_SET_COMMENT:/* -,Lock,Name,Comment Bool */
{
register RAMFILE *ramfile;
RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
char *ptr;
btos(packet->dp_Arg3, buf);
if (ramfile = searchpath(&parentdir,buf,&ptr)) {
btos(packet->dp_Arg4, buf);
if (ramfile->comment)
FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC);
strcpy(ramfile->comment, buf);
} else {
if (parentdir)
error = ERROR_OBJECT_NOT_FOUND;
else
error = ERROR_INVALID_COMPONENT_NAME;
}
}
break;
case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName Bool */
{
register RAMFILE *file1;
RAMFILE *sourcedir = getlockfile(packet->dp_Arg1);
RAMFILE *destdir = getlockfile(packet->dp_Arg3);
char *ptr;
btos(packet->dp_Arg2,buf);
dbprintf("\nRENAME '%s' (%ld) ", buf, strlen(buf));
if (file1 = searchpath(&sourcedir,buf,NULL)) {
btos(packet->dp_Arg4,buf);
dbprintf("TO '%s' (%ld)", buf, strlen(buf));
if (searchpath(&destdir,buf,&ptr)) {
error = ERROR_OBJECT_EXISTS;
} else {
if (destdir) {
if (file1 == destdir) { /* moving inside self */
error = ERROR_OBJECT_IN_USE;
break;
}
dbprintf("REN '%s' %ld", ptr, strlen(ptr));
DateStamp(&sourcedir->date);
DateStamp(&destdir->date);
/*FreeMem(file1->name, strlen(file1->name)+1);*/
Remove(file1);
file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC);
file1->parent = destdir;
strcpy(file1->name, ptr);
AddHead(&destdir->list, file1);
} else {
error = ERROR_INVALID_COMPONENT_NAME;
}
}
} else {
if (sourcedir)
error = ERROR_OBJECT_NOT_FOUND;
else
error = ERROR_INVALID_COMPONENT_NAME;
}
}
break;
/*
* A few other packet types which we do not support
*/
case ACTION_INHIBIT: /* Bool Bool */
/* Return success for the hell of it */
break;
case ACTION_RENAME_DISK:/* BSTR:NewName Bool */
case ACTION_MORECACHE: /* #BufsToAdd Bool */
case ACTION_WAIT_CHAR: /* Timeout, ticks Bool */
case ACTION_FLUSH: /* writeout bufs, disk motor off */
case ACTION_RAWMODE: /* Bool(-1:RAW 0:CON) OldState */
default:
error = ERROR_ACTION_NOT_KNOWN;
break;
}
/*#endif*/
if (packet) {
if (error) {
dbprintf("ERR=%ld\n", error);
packet->dp_Res1 = DOS_FALSE;
packet->dp_Res2 = error;
} else {
dbprintf("RES=%06lx\n", packet->dp_Res1);
}
/* #ifdef BEDEVICE */
returnpacket(packet);
/*#endif */
}
forgotten:
}
}
dbprintf("Can we remove ourselves? ");
Delay(50); /* I wanna even see the debug message! */
Forbid();
if (packetsqueued(DosProc) || GetHead(&FHBase) || GetHead(&LCBase)
|| GetHead(&RFRoot.list)) {
Permit();
dbprintf(" .. not yet!\n");
goto top; /* sorry... can't exit */
}
/*
* Causes a new process to be created on next reference
*/
DosNode->dn_Task = FALSE;
/*
* Remove Volume entry. Since DOS uses singly linked lists, we
* must (ugg) search it manually to find the link before our
* Volume entry.
*/
{
DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
register DEVLIST *dl;
register void *dlp;
dlp = &di->di_DevInfo;
for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next))
dlp = &dl->dl_Next;
if (dl == DevList) {
*(BPTR *)dlp = dl->dl_Next;
dosfree(dl);
} else {
dbprintf("****PANIC: Unable to find volume node\n");
}
}
/*
* Remove debug process, closedown, fall of the end of the world
* (which is how you kill yourself if a PROCESS. A TASK would have
* had to RemTask(NULL) itself).
*/
dbuninit();
CloseLibrary(DOSBase);
}
/*
the way this works is as follows:
[ page 273 of Amiga Dos Tech Ref Man (lower of the 2 paragraphs) ]
there is the idea of a user "FileHandle" - different from "MYFH"
when you call _LVORead it takes the user FileHandle and extracts
from it FileHandle->Arg1. Where Arg1 is "MYFH" - in this case
as Matt prosaically refers to it "My File Handle". This is what
we receive here. MYFH was created by our own OPEN command, and
thus is our own completely private entity; and all we ever see
during the read - actually all we ever see ever.
presumably the _LVOOpen command stashes MYFH into the user FileHandle
after calling us (on success).
anyway this is non obvious and the subtle difference between
user-FileHandle and Handler-FileHandle is not delinated whatsoever.
read/write
- (must ignore Virtual on/off flag)
- if *special* set only:
- extract device id (MYFH->dev), real file handle (MYFH->special)
- put real file-handle (MYFH->special) into packet->arg 1
- patch packet directly to real device (as per device id)
- later may copy packet, but then i have to send it and wait for rep
*/
andycode(name,mfh,packet)
char *name;
MYFH *mfh;
PACKET *packet;
{
#ifdef BEVIRTUAL
if(mfh->device)
{
PACKET copypacket;
bmov(packet,©packet,sizeof(PACKET));
copypacket.dp_Arg1 = mfh->devfh;
dbprintf("accessing %s\n",name);
if(!(sendpacket(©packet,mfh->device)))
{
dbprintf("Holy Shit Batman!\n");
}
packet->dp_Res1 = copypacket.dp_Res1;
dbprintf("accessing done %s\n",name);
return(1);
}
else return(0);
#else
return(0);
#endif
}
/*
* PACKET ROUTINES. Dos Packets are in a rather strange format as you
* can see by this and how the PACKET structure is extracted in the
* GetMsg() of the main routine.
*/
void
returnpacket(packet)
register struct DosPacket *packet;
{
register struct Message *mess;
register struct MsgPort *replyport;
replyport = packet->dp_Port;
mess = packet->dp_Link;
packet->dp_Port = &DosProc->pr_MsgPort;
mess->mn_Node.ln_Name = (char *)packet;
mess->mn_Node.ln_Succ = NULL;
mess->mn_Node.ln_Pred = NULL;
PutMsg(replyport, mess);
}
/*
* Are there any packets queued to our device?
*/
packetsqueued()
{
return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
(void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
}
/*
* DOS MEMORY ROUTINES
*
* DOS makes certain assumptions about LOCKS. A lock must minimally be
* a FileLock structure, with additional private information after the
* FileLock structure. The longword before the beginning of the structure
* must contain the length of structure + 4.
*
* NOTE!!!!! The workbench does not follow the rules and assumes it can
* copy lock structures. This means that if you want to be workbench
* compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
*/
void *
dosalloc(bytes)
register ulong bytes;
{
register ulong *ptr;
bytes += 4;
ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
*ptr = bytes;
return(ptr+1);
}
dosfree(ptr)
register ulong *ptr;
{
--ptr;
FreeMem(ptr, *ptr);
}
/*
* Convert a BSTR into a normal string.. copying the string into buf.
* I use normal strings for internal storage, and convert back and forth
* when required.
*/
void
btos(bstr,buf)
ubyte *bstr;
ubyte *buf;
{
bstr = BTOC(bstr);
bmov(bstr+1,buf,*bstr);
buf[*bstr] = 0;
}
/*
* Some EXEC list handling routines not found in the EXEC library.
*/
void *
NextNode(node)
NODE *node;
{
node = node->mln_Succ;
if (node->mln_Succ == NULL)
return(NULL);
return(node);
}
void *
GetHead(list)
LIST *list;
{
if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
return(list->mlh_Head);
return(NULL);
}
/*
* Compare two names which are at least n characters long each,
* ignoring case.
*/
nccmp(p1,p2,n)
register ubyte *p1, *p2;
register short n;
{
while (--n >= 0) {
if ((p1[n]|0x20) != (p2[n]|0x20))
return(0);
}
return(1);
}
/*
* Create a file or directory and link it into it's parent directory.
*/
RAMFILE *
createramfile(parentdir, type, name)
RAMFILE *parentdir;
char *name;
{
register RAMFILE *ramfile;
ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC);
AddTail(&parentdir->list, ramfile);
ramfile->parent = parentdir;
ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC);
strcpy(ramfile->name, name);
ramfile->type = type;
ramfile->protection = 0;
NewList(&ramfile->list);
DateStamp(&ramfile->date);
DateStamp(&ramfile->parent->date);
return(ramfile);
}
/*
* Free all data associated with a file
*/
void
freedata(ramfile)
RAMFILE *ramfile;
{
FENTRY *fen;
TotalBytes -= ramfile->bytes;
while (fen = RemHead(&ramfile->list)) {
dbprintf("FREE FEN: %08lx %08lx %ld\n", fen, fen->buf, fen->bytes);
FreeMem(fen->buf, fen->bytes);
FreeMem(fen, sizeof(*fen));
}
ramfile->bytes = 0;
DateStamp(&ramfile->date);
DateStamp(&ramfile->parent->date);
}
/*
* Unlink and remove a file. Any data associated with the file or
* directory has already been freed up.
*/
void
freeramfile(ramfile)
RAMFILE *ramfile;
{
Remove(ramfile); /* unlink from parent directory */
if (ramfile->name)
FreeMem(ramfile->name,strlen(ramfile->name)+1);
if (ramfile->comment)
FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
FreeMem(ramfile,sizeof(*ramfile));
}
/*
* The lock function. The file has already been checked to see if it
* is lockable given the mode.
*/
LOCK *
ramlock(ramfile, mode)
RAMFILE *ramfile;
{
LOCK *lock = dosalloc(sizeof(LOCK));
LOCKLINK *ln;
if (mode != ACCESS_WRITE)
mode = ACCESS_READ;
ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC);
AddHead(&LCBase,ln);
ln->lock = lock;
lock->fl_Link= (long)ln;
lock->fl_Key = (long)ramfile;
lock->fl_Access = mode;
lock->fl_Task = &DosProc->pr_MsgPort;
lock->fl_Volume = (BPTR)CTOB(DosNode);
if (mode == ACCESS_READ)
++ramfile->locks;
else
ramfile->locks = -1;
return(lock);
}
void
ramunlock(lock)
LOCK *lock;
{
RAMFILE *file = (RAMFILE *)lock->fl_Key;
Remove(lock->fl_Link); /* unlink from list */
FreeMem(lock->fl_Link, sizeof(LOCKLINK)); /* free link node */
if (lock->fl_Access == ACCESS_READ) /* undo lock effect */
--file->locks;
else
file->locks = 0;
dosfree(lock); /* free lock */
}
/*
* GETLOCKFILE(bptrlock)
*
* Return the RAMFILE entry (file or directory) associated with the
* given lock, which is passed as a BPTR.
*
* According to the DOS spec, the only way a NULL lock will ever be
* passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
* In anycase, If a NULL lock is passed to me I simply assume it means
* the root directory of the RAM disk.
*/
RAMFILE *
getlockfile(lock)
void *lock; /* actually BPTR to LOCK */
{
register LOCK *rl = BTOC(lock);
if (rl)
return((RAMFILE *)rl->fl_Key);
return(&RFRoot);
}
/*
* Search the specified path beginning at the specified directory.
* The directory pointer is updated to the directory containing the
* actual file. Return the file node or NULL if not found. If the
* path is illegal (an intermediate directory was not found), set *ppar
* to NULL and return NULL.
*
* *ppar may also be set to NULL if the search path IS the root.
*
* If pptr not NULL, Set *pptr to the final component in the path.
*/
RAMFILE *
searchpath(ppar,buf,pptr)
RAMFILE **ppar;
char *buf;
char **pptr;
{
RAMFILE *file = *ppar;
RAMFILE *srch;
short len;
char *ptr;
*ppar = NULL;
for (;*buf && file;) {
ptr = getpathelement(&buf,&len);
if (buf[0] == ':') { /* go to root */
++buf;
file = &RFRoot;
continue;
}
if (*ptr == '/') { /* go back a directory */
if (!file->parent) { /* no parent directory */
return(NULL);
}
file = file->parent;
continue;
}
if (file->type == FILE_FILE)
return(NULL);
for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
file = srch; /* element found */
break;
}
}
if (srch == NULL) {
if (*buf == 0) /* Element not found. If it was the final */
*ppar = file; /* element the parent directory is valid */
if (pptr)
*pptr = ptr;
return(NULL);
}
}
if (pptr)
*pptr = ptr;
*ppar = file->parent;
return(file);
}
/*
* Return the next path element in the string. The routine effectively
* removes any trailing '/'s, but treats ':' as part of the next component
* (i.e. ':' is checked and skipped in SEARCHPATH()).
*/
char *
getpathelement(pstr,plen)
char **pstr;
short *plen;
{
char *base;
register char *ptr = *pstr;
register short len = 0;
if (*(base = ptr)) {
if (*ptr == '/') {
++ptr;
++len;
} else {
while (*ptr && *ptr != '/' && *ptr != ':') {
++ptr;
++len;
}
if (*ptr == '/')
++ptr;
}
}
*pstr = ptr;
*plen = len;
return(base);
}
char *
typetostr(ty)
{
switch(ty) {
case ACTION_DIE: return("DIE");
case ACTION_OPENRW: return("OPEN-RW");
case ACTION_OPENOLD: return("OPEN-OLD");
case ACTION_OPENNEW: return("OPEN-NEW");
case ACTION_READ: return("READ");
case ACTION_WRITE: return("WRITE");
case ACTION_CLOSE: return("CLOSE");
case ACTION_SEEK: return("SEEK");
case ACTION_EXAMINE_NEXT: return("EXAMINE NEXT");
case ACTION_EXAMINE_OBJECT: return("EXAMINE OBJ");
case ACTION_INFO: return("INFO");
case ACTION_DISK_INFO: return("DISK INFO");
case ACTION_PARENT: return("PARENTDIR");
case ACTION_DELETE_OBJECT: return("DELETE");
case ACTION_CREATE_DIR: return("CREATEDIR");
case ACTION_LOCATE_OBJECT: return("LOCK");
case ACTION_COPY_DIR: return("DUPLOCK");
case ACTION_FREE_LOCK: return("FREELOCK");
case ACTION_SET_PROTECT: return("SETPROTECT");
case ACTION_SET_COMMENT: return("SETCOMMENT");
case ACTION_RENAME_OBJECT: return("RENAME");
case ACTION_INHIBIT: return("INHIBIT");
case ACTION_RENAME_DISK: return("RENAME DISK");
case ACTION_MORECACHE: return("MORE CACHE");
case ACTION_WAIT_CHAR: return("WAIT FOR CHAR");
case ACTION_FLUSH: return("FLUSH");
case ACTION_RAWMODE: return("RAWMODE");
default: return("---------UNKNOWN-------");
}
}
/*
* DEBUGGING CODE. You cannot make DOS library calls that access other
* devices from within a DOS device driver because they use the same
* message port as the driver. If you need to make such calls you must
* create a port and construct the DOS messages yourself. I do not
* do this. To get debugging info out another PROCESS is created to which
* debugging messages can be sent.
*
* You want the priority of the debug process to be larger than the
* priority of your DOS handler. This is so if your DOS handler crashes
* you have a better idea of where it died from the debugging messages
* (remember that the two processes are asyncronous from each other).
*/
extern void debugproc();
dbinit()
{
TASK *task = FindTask(NULL);
if (!Dbport) {
Dback = CreatePort(NULL,NULL);
if((CreateProc("DEV_DB", task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096)))
{
WaitPort(Dback); /* handshake startup */
GetMsg(Dback); /* remove dummy msg */
dbprintf("Debugger running V1.10, 2 November 1987\n");
dbprintf("Works with WORKBENCH!\n");
}
else
DeletePort(Dback);
}
}
dbuninit()
{
MSG killmsg;
if (Dbport) {
killmsg.mn_Length = 0; /* 0 means die */
PutMsg(Dbport,&killmsg);
WaitPort(Dback); /* He's dead jim! */
GetMsg(Dback);
DeletePort(Dback);
Dbport = 0;
/*
* Since the debug process is running at a greater priority, I
* am pretty sure that it is completely removed
* before this task gets control again. Still, it doesn't hurt...
*/
Delay(50); /* ensure he's dead */
}
}
dbprintf(a,b,c,d,e,f,g,h,i,j)
{
char buf[256];
MSG *msg;
if (Dbport && !DBDisable) {
sprintf(buf,a,b,c,d,e,f,g,h,i,j);
msg = AllocMem(sizeof(MSG)+strlen(buf)+1, MEMF_PUBLIC|MEMF_CLEAR);
msg->mn_Length = strlen(buf)+1; /* Length NEVER 0 */
strcpy(msg+1,buf);
PutMsg(Dbport,msg);
}
}
/*
* BTW, the DOS library used by debugmain() was actually openned by
* the device driver. Note: DummyMsg cannot be on debugmain()'s stack
* since debugmain() goes away on the final handshake.
*/
debugmain()
{
MSG *msg;
short len;
void *fh;
Dbport = CreatePort(NULL,NULL);
fh = Open("con:0/0/640/100/debugwindow", 1006);
PutMsg(Dback, &DummyMsg);
for (;;) {
WaitPort(Dbport);
msg = GetMsg(Dbport);
len = msg->mn_Length;
if (len == 0)
break;
--len; /* Fix length up */
Write(fh, msg+1, len);
FreeMem(msg,sizeof(MSG)+len+1);
}
Close(fh);
DeletePort(Dbport);
PutMsg(Dback,&DummyMsg); /* Kill handshake */
}
#ifdef BEVIRTUAL
#ifdef TESTING
/*
* Patch Data I/O functions directly to owner device
*
*/
void
patchpacket2()
{
for(;;)
{
for(x=0;x<9999;x++)
{
if(!getnextdev("name")) break;
if(dev->VolDays != mydev->VolDays)
continue;
else
{
/* We found the device, now patch thru to it */
/* AmigaDOS-TR p271: "AmigaDOS maintains all other fields" */
/* so I guess, being AmigaDOS, I have to be nice */
/*
- copy the packet
- replace name with original full path name
- issue the packet
- wait for reply
- return reply to host
*/
}
}
/* if(!(Request("Insert Disk X")))break; else continue; */
}
}
/* to send a packet:
struct MsgPort *replyport;
replyport = (struct MsgPort *) CreatePort(NULL,0);
if(!replyport) return(0L);
PutMsg(pid,(struct Message *)packet);
WaitPort(replyport);
GetMsg(replyport);
DeletePort(replyport);
note that deviceproc does quite a handy job of requesting a name
perhaps it calls some semi-generic routine?
*/
{
register struct Message *mess;
register struct MsgPort *replyport;
mess = packet->dp_Link;
mess->mn_Node.ln_Name = (char *)packet;
mess->mn_Node.ln_Succ = NULL;
mess->mn_Node.ln_Pred = NULL;
PutMsg(MyDevNode->dn_Task,mess);
/* now forget about the packet completely! */
goto forgotten;
}
#ifdef BETESTING
/*********************************************************/
{
register struct Message *mmouess;
register struct MsgPort *replyport;
DEVNODE *MyDevNode;
if(!(MyDevNode = FindHandler("scratch:",DOSBase))) /* and key */
{ dbprintf("Device not found!\n"); }
else
{
mess = packet->dp_Link;
mess->mn_Node.ln_Name = (char *)packet;
mess->mn_Node.ln_Succ = NULL;
mess->mn_Node.ln_Pred = NULL;
PutMsg(MyDevNode->dn_Task,mess);
/* now forget about the packet completely! */
/*goto forgotten;*/
}
}
/*********************************************************/
#endif
/*
* rather than replying to a packet; pass it off to its real
* destination, in such a way that the client feels like he is
* talking directly with the third party - this permits me to
* drop out of the process without having to handle to crosstalk. AH
(this function is commented out for the moment in favour of the
absolutely most robust solution [ see sendpacket below ]...later on
if this is stable i may turn this back on...its moderately faster
and mongo coolo.
*/
patchpacket(packet,mdest)
register struct DosPacket *packet;
register struct MsgPort *mdest;
{
register struct Message *mess;
mess = packet->dp_Link; /* address of packet */
/* packet->dp_Port */ /* originator same!!*/
mess->mn_Node.ln_Name = (char *)packet; /* my message */
mess->mn_Node.ln_Succ = NULL;
mess->mn_Node.ln_Pred = NULL;
PutMsg(mdest,mess); /* don't wait!!! */
}
#endif TESTING
/*
* send a packet to a third party, but act as a mediator
* intercepting the reply and returning with it to higher
* level routines which will act based on response. AH
*
*/
sendpacket(packet,dest)
register struct DosPacket *packet;
register struct MsgPort *dest;
{
register struct Message *mess;
struct MsgPort *replyport;
replyport = (struct MsgPort *) CreatePort(NULL,0);
if(!replyport) return(0L);
mess = packet->dp_Link;
packet->dp_Port = replyport; /* comes back to me */
mess->mn_Node.ln_Name = (char *)packet;
mess->mn_Node.ln_Succ = NULL;
mess->mn_Node.ln_Pred = NULL;
PutMsg(dest,mess);
WaitPort(replyport);
GetMsg(replyport);
DeletePort(replyport);
return(1);
}
/*
* read from my own ram disk. AH
*/
int
myreadsome(mptr,mfen,mleft) /* return total read */
ubyte *mptr; /* ptr = destination */
FENTRY *mfen; /* node list of bits of RAM */
int mleft; /* desired amount */
{
register long scr; long *start = mptr;
int mtotal; /* always returns 0? bug */
mtotal = 0;
dbprintf("readsome left, fen->bytes %ld %ld\n",mleft,mfen->bytes);
while (mleft && mfen) {
scr = mfen->bytes; /* bytes here */
if (mleft < scr) { /* desired < available */
bmov(mfen->buf, mptr, mleft);
mtotal += mleft;
mleft = 0; /* done! */
}
else { /* move what I can, goto next node */
bmov(mfen->buf, mptr, scr);
mleft -= scr; /* decrease desired */
mtotal += scr;
mptr += scr; /* increase write dest */
mfen = NextNode(mfen);
}
}
/* once i've got the file just check here to make sure its a virtual instance
normally I only read around a hundred bytes...so even if this isn't a virtual
file there isn't a tremendous wastage of overhead... */
if ( *start !=VIRT) return(0);
dbprintf("total is %ld\n",mtotal);
return(mtotal);
}
#endif
#ifdef COMMENT
/* Returned by Examine() and ExNext(), must be on a 4 byte boundary */
struct FileInfoBlock {
LONG fib_DiskKey;
LONG fib_DirEntryType; /* Type of Directory. If < 0, then a plain file.
* If > 0 a directory */
char fib_FileName[108]; /* Null terminated. Max 30 chars used for now */
LONG fib_Protection; /* bit mask of protection, rwxd are 3-0. */
LONG fib_EntryType;
LONG fib_Size; /* Number of bytes in file */
LONG fib_NumBlocks; /* Number of blocks in file */
struct DateStamp fib_Date;/* Date file last changed */
char fib_Comment[80]; /* Null terminated comment associated with file */
char fib_Reserved[36];
}; /* FileInfoBlock */
#endif
/*
* need to check for the key also
- need to put up a "please insert disk x":
this requestor should have a variable length timeout
this requestor should accept an alternate file path
this requestor should have button for "just use df0:"
cancel and accept will also be there
as well i must keep perusing the device list...
* need to imbed the checksum in returned fib (somewhere safe)
* if file is not virtual do i call myreadsome anyway??? (YES)
- any weaknesses in matts driver in general?
- a compile may fail if buffer is not longword aligned.
may want to have some kind of runtime, or half-compile time check!
- device driver should checksum itself, and checksum its files, being
a ram device and prone to external trashing
- matt screws around with the diskkey...he shouldn't do that...
- replace viblen with sizeofvib
- there *must* be a "bestupid" mode which checks with much less rigour
- when i find a devnode i should lock it (but how do i do that???)
* blocks used is buggy
* what if the vir: passes of a packet and the silly dest isn't there?
does vir: close its own copy????
*/